package cn.zhaosunny.soap

import cn.zhaosunny.soap.exception.SoapException
import cn.zhaosunny.soap.exception.SoapHttpException
import cn.zhaosunny.soap.internal.SoapHttp
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import io.reactivex.Flowable
import io.reactivex.Observable
import kotlinx.coroutines.flow.Flow
import okhttp3.OkHttpClient
import java.lang.Exception
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Proxy
import java.lang.reflect.Type
import kotlin.coroutines.Continuation

/**
 * 重写Soap工具
 * 基于okhttp 用于请求Soap请求 <p>
 * 支持Flowable,Observable,Flow,协程 <p>
 * 返回数据支持ReturnMode
 *
 * <code>
 *  val soap = Soap.Builder(baseUrl).build()
 * </code>
 * @author zhaoyang 2021/11/30
 */
class Soap {

    class SoapBuilder {
        private val soap by lazy { Soap() }

        fun setBaseUrl(url: String): SoapBuilder {
            soap.baseUrl = url
            return this
        }

        fun setInterceptor(interceptor: ISoapInterceptor): SoapBuilder {
            soap.interceptor = interceptor
            return this
        }

        fun setCallFactory(callFactory: CallFactory): SoapBuilder {
            soap.callFactory = callFactory
            return this
        }

//        fun setConvertFactory(convertFactory: ConvertFactory<Any, Any>): SoapBuilder {
//            soap.convertFactory = convertFactory
//            return this
//        }

        fun setIsNewJdk8(boolean: Boolean): SoapBuilder {
            soap.isNewForJDK8 = boolean
            return this
        }

        fun setOkhttpClient(okHttpClient: OkHttpClient): SoapBuilder {
            soap.okHttpClient = okHttpClient
            return this
        }

        /**
         * 用于校验是否为错误
         */
        fun setErrorVerify(service: IErrorStateService): SoapBuilder {
            soap.errorStateService = service
            return this
        }

        fun build(): Soap {
            return soap
        }
    }

    /**
     * 配置BaseUrl地址
     */
    var baseUrl: String = ""

    /**
     * 配置数据监听
     */
    var interceptor: ISoapInterceptor? = null

    /**
     * 数据请求转换器
     */
    var callFactory: CallFactory = DefaultCallFactory()


    /**
     * 部分后台升级，请求失败，可开启此配置
     */
    var isNewForJDK8: Boolean = false


    /**
     *  okHttpClient
     */
    var okHttpClient: OkHttpClient = OkHttpClient()

    /**
     * 判断是否为错误信息
     */
    var errorStateService: IErrorStateService? = null


    /**
     * 可以独立配置
     */
    var gson: Gson = Gson()

    fun convertResult(result: String, type: Type): Any {
        if (type == String::class.java) {
            return result
        }
        if (result.isEmpty()) {
            throw SoapException("查询数据为空")
        }
        val adapter: TypeAdapter<*> = gson.getAdapter(TypeToken.get(type))
        return adapter.fromJson(result)
    }


    @Suppress("unchecked_cast")
    fun <T> create(clazz: Class<T>): T {
        return Proxy.newProxyInstance(this.javaClass.classLoader,
            arrayOf(clazz)) { _, method, args ->
            val soapRequest = SoapRequest(clazz, method, args)
            if (method.returnType == (String::class.java)) {
                callFactory.call(this, soapRequest)
            } else if (method.returnType == Observable::class.java) {
                val genericReturnType = method.genericReturnType as ParameterizedType
                val type = genericReturnType.actualTypeArguments[0]
                callFactory.observableCall(this, soapRequest, type)
            } else if (method.returnType == Flowable::class.java) {
                val genericReturnType = method.genericReturnType as ParameterizedType
                val type = genericReturnType.actualTypeArguments[0]
                callFactory.flowableCall(this, soapRequest, type)
            } else if (method.returnType == Flow::class.java) {
                val genericReturnType = method.genericReturnType as ParameterizedType
                val type = genericReturnType.actualTypeArguments[0]
                callFactory.flowCall(this, soapRequest, type)
            } else if (isKotlinCall(method)) {
                callFactory.call(args.last() as Continuation<Any?>,
                    this,
                    soapRequest,
                    SoapHttp.getKotinRawType(method))
            } else {
                method.invoke(args)
            }
        } as T
    }


}